/*	
 *				
 *					ISNW ---- It is a war.It is not warcraft!
 *
 *
 *					Author:					zhoukeli(haxixi_keli)
 *					Filename:				triangle3d.h
 *					CreateTime:				2008/12/10
 *					ModifyTime:				2008/12/10
 *
 */

#ifndef __ISNW_TRIANGLE_3D_H__
#define __ISNW_TRIANGLE_3D_H__

#include "isnwTypes.h"
#include "vector3d.h"
#include "line3d.h"
#include "plane3d.h"
#include "aabbox3d.h"

namespace ISNW
{
namespace core
{
	template<typename T>
	class triangle3d
	{
	public:
		//! constructor
		triangle3d():pointA(1,0,0),pointB(0,1,0),pointC(0,0,1){}
		triangle3d(const vector3d<T>& a,const vector3d<T>& b,const vector3d<T>& c){set(a,b,c);}
		triangle3d(const triangle3d<T>& other){set(other);}

	public:
		//! operator
		triangle3d<T>& operator = (const triangle3d<T>& other){set(other);}

		bool operator == (const triangle3d<T>& other) const{return equal(other);}
		bool operator != (const triangle3d<T>& other) const{return !equal(other);}

		triangle3d<T>& operator += (const vector3d& val)
		{
			pointA += val;
			pointB += val;
			pointC += val;

			return *this;
		}
		triangle3d<T> operator + (const vector3d& val)const{return triangle3d<T>(pointA+val,pointB+val,pointC+val);}

		triangle3d<T>& operator -= (const vector3d& val){return (*this += -val);}
		triangle3d<T> operator - (const vector3d& val)const{return (*this + -val);}

	public:
		//! function
		triangle3d<T>& set(const triangle3d<T>& other)
		{
			pointA = other.pointA;
			pointB = other.pointB;
			pointC = other.pointC;

			return *this;
		}

		triangle3d<T>& set(const vector3d<T>& a,const vector3d<T>& b,const vector3d<T>& c)
		{
			pointA = a;
			pointB = b;
			pointC = c;

			return *this;
		}

		bool equal(const triangle3d<T>& other) const
		{
			return (pointA == other.pointA && pointB == other.pointB && pointC == other.pointC);
		}

		bool istotalinsidebox(const aabbox3d<T>& box) const
		{
			return (box.ispointinside(pointA)&&box.ispointinside(pointB)&&box.ispointinside(pointC));
		}

		vector3d<T> getclosedpoint(const vector3d<T> point) const
		{
			vector3d<T> ab = line3d<T>(pointA,pointB).getclosedpoint(point);
			vector3d<T> ac = line3d<T>(pointA,pointC).getclosedpoint(point);
			vector3d<T> bc = line3d<T>(pointB,pointC).getclosedpoint(point);

			T ab_p = ab.getdistancefromSQ(point);
			T ac_p = ac.getdistancefromSQ(point);
			T bc_p = bc.getdistancefromSQ(point);

			if(ab_p > ac_p)
				return ac_p < bc_p ? ac : bc;

			return ab_p < bc_p ? ab : bc;
		}

		bool ispointinside(const vector3d<T> point) const
		{
			if(line3d<T>(pointA,pointB).isonsameside(pointC,point)
				&& line3d<T>(pointA,pointC).isonsameside(pointB,point)
				&& line3d<T>(pointB,pointC).isonsameside(pointA,point))
				return true;

			return false;
		}

		vector3d<T> getnormal() const
		{
			return ((pointB - pointA).crossproduct(pointC - pointA));
		}
		
		bool getintersectionwithline(const vector3d<T>& linepoint,
					const vector3d<T>& dirction,
					vector3d<T>& out) const
		{
			plane3d<T> plane(pointA,pointB,pointC);

			if(plane.getIntersectionWithLine(linepoint,dirction,out))
				return ispointinside(out);

			return false;
		}

		bool getintersectionwithlimitedline(const line3d<T>& line,vector3d<T>& out) const
		{
			plane3d<T> plane(pointA,pointB,pointC);

			if(plane.getIntersectionWithLimitedLine(line,out))
				return ispointinside(out);

			return false;
		}

		bool getintersectionwithtriangle(const triangle3d<T>& tri)
		{
			if(*this == tri) return true;

			vector3d<T> tmp_out;
			if(getintersectionwithlimitedline(line3d<T>(tri.pointA,tri.pointB),tmp_out)
				&& getintersectionwithlimitedline(line3d<T>(tri.pointB,tri.pointC),tmp_out)
				&& getintersectionwithlimitedline(line3d<T>(tri.pointA,tri.pointC),tmp_out))
				return true;

			return false;
		}

		bool isfrontface(const vector3d<T>& direction) const
		{
			vector3d<T> normal = getnormal().normalize();

			if(normal.dotproduct(direction)) return true;

			return false;
		}

		T getarea() const
		{
			vector3d<T> cross = (pointB - pointA).crossproduct(pointC - pointA);

			return cross.getlength() * 0.5f;
		}

		plane3d<T> getplane() const
		{
			return plane3d<T>(pointA,pointB,pointC);
		}

	public:
		vector3d<T> pointA;
		vector3d<T> pointB;
		vector3d<T> pointC;
	};

	//! typedef for float triangle
	typedef triangle3d<f32> triangle3df;
	//! typedef for int triangle
	typedef triangle3d<s32> triangle3di;

} // end namespace core
} // end namespace ISNW
#endif